package logv

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

type Logger struct {
	logger     zerolog.Logger
	logDir     string
	currentDay string
	file       *os.File
	mu         sync.RWMutex
	hooks      []Hook // 钩子列表
}

var (
	defaultLogger *Logger
	once          sync.Once
)

// 初始化默认日志器
func init() {
	once.Do(func() {
		defaultLogger = NewLogger("logs")
		// 配置全局zerolog输出，使log.Info()等方法也能写入文件
		// 延迟调用，确保文件已经打开
		if defaultLogger != nil {
			setupGlobalLogger()
		}
	})
}

// NewLogger 创建新的日志器
func NewLogger(logDir string) *Logger {
	l := &Logger{
		logDir: logDir,
	}
	
	// 确保日志目录存在
	if err := os.MkdirAll(logDir, 0755); err != nil {
		log.Fatal().Err(err).Msg("Failed to create log directory")
	}
	
	// 初始化日志文件
	l.rotateLogFile()
	
	return l
}

// rotateLogFile 轮转日志文件
func (l *Logger) rotateLogFile() {
	l.mu.Lock()
	defer l.mu.Unlock()
	
	today := time.Now().Format("2006-01-02")
	
	// 如果是同一天，不需要轮转
	if l.currentDay == today && l.file != nil {
		return
	}
	
	// 关闭旧文件
	if l.file != nil {
		l.file.Close()
	}
	
	// 创建新的日志文件
	filename := fmt.Sprintf("%s.txt", today)
	filepath := filepath.Join(l.logDir, filename)
	
	file, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal().Err(err).Str("file", filepath).Msg("Failed to open log file")
	}
	
	l.file = file
	l.currentDay = today
	
	// 创建多输出writer（同时输出到文件和控制台）
	multi := io.MultiWriter(os.Stdout, file)
	
	// 配置zerolog
	l.logger = zerolog.New(multi).With().
		Timestamp().
		Caller().
		Logger()
	
	// 设置日志级别
	zerolog.SetGlobalLevel(zerolog.InfoLevel)
	
	// 更新全局logger配置
	updateGlobalLogger()
}

// checkAndRotate 检查是否需要轮转日志文件
func (l *Logger) checkAndRotate() {
	today := time.Now().Format("2006-01-02")
	if l.currentDay != today {
		l.rotateLogFile()
	}
}

// Debug 输出调试级别日志
func (l *Logger) Debug(msg string) {
	l.checkAndRotate()
	event := l.logger.Debug()
	l.runHooks(event, zerolog.DebugLevel, msg)
	event.Msg(msg)
}

// DebugCtx 带context的调试级别日志
func (l *Logger) DebugCtx(ctx context.Context, msg string) {
	l.checkAndRotate()
	event := l.logger.Debug()
	l.addContextFields(event, ctx)
	l.runHooks(event, zerolog.DebugLevel, msg)
	event.Msg(msg)
}

// Info 输出信息级别日志
func (l *Logger) Info(msg string) {
	l.checkAndRotate()
	event := l.logger.Info()
	l.runHooks(event, zerolog.InfoLevel, msg)
	event.Msg(msg)
}

// InfoCtx 带context的信息级别日志
func (l *Logger) InfoCtx(ctx context.Context, msg string) {
	l.checkAndRotate()
	event := l.logger.Info()
	l.addContextFields(event, ctx)
	l.runHooks(event, zerolog.InfoLevel, msg)
	event.Msg(msg)
}

// Warn 输出警告级别日志
func (l *Logger) Warn(msg string) {
	l.checkAndRotate()
	event := l.logger.Warn()
	l.runHooks(event, zerolog.WarnLevel, msg)
	event.Msg(msg)
}

// WarnCtx 带context的警告级别日志
func (l *Logger) WarnCtx(ctx context.Context, msg string) {
	l.checkAndRotate()
	event := l.logger.Warn()
	l.addContextFields(event, ctx)
	l.runHooks(event, zerolog.WarnLevel, msg)
	event.Msg(msg)
}

// Error 输出错误级别日志
func (l *Logger) Error(msg string) {
	l.checkAndRotate()
	event := l.logger.Error()
	l.runHooks(event, zerolog.ErrorLevel, msg)
	event.Msg(msg)
}

// ErrorCtx 带context的错误级别日志
func (l *Logger) ErrorCtx(ctx context.Context, msg string) {
	l.checkAndRotate()
	event := l.logger.Error()
	l.addContextFields(event, ctx)
	l.runHooks(event, zerolog.ErrorLevel, msg)
	event.Msg(msg)
}

// Fatal 输出致命错误级别日志并退出程序
func (l *Logger) Fatal(msg string) {
	l.checkAndRotate()
	event := l.logger.Fatal()
	l.runHooks(event, zerolog.FatalLevel, msg)
	event.Msg(msg)
}

// FatalCtx 带context的致命错误级别日志并退出程序
func (l *Logger) FatalCtx(ctx context.Context, msg string) {
	l.checkAndRotate()
	event := l.logger.Fatal()
	l.addContextFields(event, ctx)
	l.runHooks(event, zerolog.FatalLevel, msg)
	event.Msg(msg)
}

// WithFields 添加字段
func (l *Logger) WithFields(fields map[string]interface{}) *zerolog.Event {
	l.checkAndRotate()
	event := l.logger.Info()
	for k, v := range fields {
		event = event.Interface(k, v)
	}
	return event
}

// WithFieldsCtx 带context添加字段
func (l *Logger) WithFieldsCtx(ctx context.Context, fields map[string]interface{}) *zerolog.Event {
	l.checkAndRotate()
	event := l.logger.Info()
	l.addContextFields(event, ctx)
	for k, v := range fields {
		event = event.Interface(k, v)
	}
	return event
}

// WithError 添加错误信息
func (l *Logger) WithError(err error) *zerolog.Event {
	l.checkAndRotate()
	return l.logger.Error().Err(err)
}

// WithErrorCtx 带context添加错误信息
func (l *Logger) WithErrorCtx(ctx context.Context, err error) *zerolog.Event {
	l.checkAndRotate()
	event := l.logger.Error().Err(err)
	l.addContextFields(event, ctx)
	return event
}

// addContextFields 向日志事件添加context中的字段
func (l *Logger) addContextFields(event *zerolog.Event, ctx context.Context) {
	if ctx == nil {
		return
	}
	
	// 添加追踪ID
	if traceID := GetTraceID(ctx); traceID != "" {
		event.Str("trace_id", traceID)
	}
	
	// 添加请求ID
	if requestID := GetRequestID(ctx); requestID != "" {
		event.Str("request_id", requestID)
	}
}

// Close 关闭日志器
func (l *Logger) Close() error {
	l.mu.Lock()
	defer l.mu.Unlock()
	
	if l.file != nil {
		return l.file.Close()
	}
	return nil
}

// GetDefaultLogger 获取默认日志器
func GetDefaultLogger() *Logger {
	return defaultLogger
}

// getCurrentTime 获取当前时间（用于测试时可以mock）
func getCurrentTime() time.Time {
	return time.Now()
}

// setupGlobalLogger 配置全局zerolog，使log.Info()等方法也能写入文件
func setupGlobalLogger() {
	if defaultLogger != nil && defaultLogger.file != nil {
		// 创建多输出writer（同时输出到文件和控制台）
		multi := io.MultiWriter(os.Stdout, defaultLogger.file)
		
		// 配置全局zerolog
		log.Logger = zerolog.New(multi).With().
			Timestamp().
			Caller().
			Logger()
		
		// 设置全局日志级别
		zerolog.SetGlobalLevel(zerolog.InfoLevel)
	}
}

// updateGlobalLogger 更新全局zerolog配置（当日志文件轮转时调用）
func updateGlobalLogger() {
	setupGlobalLogger()
}

// WithLogger 将配置好的logger添加到context中，使log.Ctx(ctx)能正常工作
func WithLogger(ctx context.Context) context.Context {
	if defaultLogger != nil {
		return defaultLogger.logger.WithContext(ctx)
	}
	return ctx
}

// AddHook 添加钩子
func (l *Logger) AddHook(hook Hook) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.hooks = append(l.hooks, hook)
}

// RemoveHook 移除钩子
func (l *Logger) RemoveHook(hook Hook) {
	l.mu.Lock()
	defer l.mu.Unlock()
	for i, h := range l.hooks {
		// 使用反射比较，因为函数类型不能直接比较
		if compareHooks(h, hook) {
			l.hooks = append(l.hooks[:i], l.hooks[i+1:]...)
			break
		}
	}
}

// compareHooks 比较两个钩子是否相同
func compareHooks(h1, h2 Hook) bool {
	// 对于相同的指针，直接比较
	if h1 == h2 {
		return true
	}
	
	// 对于函数类型，无法直接比较，返回 false
	// 用户需要保存钩子引用来进行移除操作
	return false
}

// ClearHooks 清空所有钩子
func (l *Logger) ClearHooks() {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.hooks = nil
}

// runHooks 执行所有钩子
func (l *Logger) runHooks(e *zerolog.Event, level zerolog.Level, msg string) {
	l.mu.RLock()
	hooks := make([]Hook, len(l.hooks))
	copy(hooks, l.hooks)
	l.mu.RUnlock()
	
	for _, hook := range hooks {
		hook.Run(e, level, msg)
	}
}